package com.project.utils.pagination;

import com.project.myBeans.EntitySupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.parameter.DefaultParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

/**
 * <p>Title: PaginationInterceptor.java</p>
 * <p>Description: 物理分页，拦截器</p>
 * <p>Copyright: </p>
 * <p>Company: </p>
 * <p>createTime:2012-6-11 下午6:39:39</p>
 *
 * @author xwf
 * @version 1.0
 */
//@Intercepts({@Signature(type=Executor.class,method="query",args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })})  
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationInterceptor implements Interceptor {
    private EntitySupport entity;

    private static final Log log = LogFactory.getLog(PaginationInterceptor.class);
    /*	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);

//		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
//		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
//			return invocation.proceed();
//		}

		DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler.getValue("delegate.parameterHandler");
//		Map parameterMap = (Map) defaultParameterHandler.getParameterObject();
		Object parameter = defaultParameterHandler.getParameterObject();    
//		Object sidx = parameterMap.get("_sidx");
//		Object sord = parameterMap.get("_sord");

		String originalSql = (String) metaStatementHandler
				.getValue("delegate.boundSql.sql");

//		if (sidx != null && sord != null) {
//			originalSql = originalSql + " order by " + sidx + " " + sord;
//		}
		
		if(!hasPaging(parameter)){    
            return invocation.proceed();    
        }  
        
        int offset=0; int limit=0;
        if(parameter instanceof EntitySupport){
        	EntitySupport entity = (EntitySupport) parameter;
        	Object sidx = entity.getSortField();    
        	Object sord = entity.getSortOrder(); 
        	offset = entity.getPageIndex()*entity.getPageSize();    
			limit = offset+entity.getPageSize(); 
			
	        if(sidx != null && sord != null){    
	            originalSql = originalSql + " order by " + sidx + " " + sord;    
	        } 
        	
        }

		Configuration configuration = (Configuration) metaStatementHandler
				.getValue("delegate.configuration");

		Dialect.Type databaseType = null;
		try {
			databaseType = Dialect.Type.valueOf(configuration.getVariables()
					.getProperty("dialect").toUpperCase());
		} catch (Exception e) {
			// ignore
		}
		if (databaseType == null) {
			databaseType = Dialect.Type.valueOf("oracle".toUpperCase());
			log.debug("没有配置的数据库类型，采用默认形式oracle");
		}else{
			log.debug("配置的数据库类型为 : " + databaseType);
		}
		Dialect dialect = null;
		String sql = "";
		switch (databaseType) {
			case ORACLE:
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
			case MYSQL:
				dialect = new DBDialect();
				sql = dialect.getMysqlString(originalSql, offset,limit);
				break;
			default:
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
		}
		if (log.isDebugEnabled()) {
			BoundSql boundSql = statementHandler.getBoundSql();
			log.debug("生成分页SQL : " + boundSql.getSql());
		}
		metaStatementHandler.setValue("delegate.boundSql.sql", sql);
		metaStatementHandler.setValue("delegate.rowBounds.offset",
				RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit",
				RowBounds.NO_ROW_LIMIT);
		
		return invocation.proceed();
	}*/

    /**
     * 拦截器执行的内容
     *
     * @param invocation 动态代理的3个参数
     * @return
     * @throws Throwable
     * @date 2012-6-21 上午9:52:01
     * @author cxp
     */
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);

//		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
//		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
//			return invocation.proceed();
//		}

        DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler.getValue("delegate.parameterHandler");
//		Map parameterMap = (Map) defaultParameterHandler.getParameterObject();
        Object parameter = defaultParameterHandler.getParameterObject();//得到的是mybatis传入的对象
//		Object sidx = parameterMap.get("_sidx");
//		Object sord = parameterMap.get("_sord");

        String originalSql = (String) metaStatementHandler
                .getValue("delegate.boundSql.sql");
        String originalSql2 = originalSql;//用于查询count的sql
//		if (sidx != null && sord != null) {
//			originalSql = originalSql + " order by " + sidx + " " + sord;
//		}

        if (!hasPaging(parameter)) {
            return invocation.proceed();//直接执行，跳过下面的内容
        }

        int offset = 0;
        int limit = 0;
        int total = 0;
        EntitySupport entity = null;
        if (parameter instanceof EntitySupport) {
            entity = (EntitySupport) parameter;
            Object sidx = entity.getSortField();
            Object sord = entity.getSortOrder();
            offset = entity.getPageIndex() * entity.getPageSize();
            limit = entity.getPageSize();
            total = entity.getTotal();
			/* oracle
			if(sidx!=null&&!"".equals(sidx)){
				//已用非row_number函数排序过的
				if(originalSql.toUpperCase().indexOf("ORDER")>=0 && originalSql.toUpperCase().indexOf("ROW_NUMBER") == 0){
					originalSql = originalSql + "," + sidx;
				}else{
					originalSql = originalSql + " order by " + sidx;
				}

				if(sord!=null&&!"".equals(sord)){
					originalSql = originalSql + " " + sord;
				}
			}*/

            if (sidx != null && !"".equals(sidx)) {
                //已用非row_number函数排序过的
                if (originalSql.toUpperCase().indexOf("ORDER") >= 0) {
                    originalSql = "select * from (" + originalSql + ") tb_alias order by " + sidx;
                } else {
                    originalSql = originalSql + " order by " + sidx;
                }

                if (sord != null && !"".equals(sord)) {
                    originalSql = originalSql + " " + sord;
                }
            }

        }

        Configuration configuration = (Configuration) metaStatementHandler
                .getValue("delegate.configuration");

        BoundSql boundSql = statementHandler.getBoundSql();
        // 得到总记录数
        if (total == 0) {
            StringBuffer countSql = new StringBuffer(originalSql.length() + 100);
            countSql.append("select count(1) from (").append(originalSql2)
                    .append(") t");
            Connection connection = (Connection) invocation.getArgs()[0];
            PreparedStatement countStmt = connection.prepareStatement(countSql
                    .toString());
            //BoundSql countBS = new BoundSql(configuration,countSql.toString(), boundSql.getParameterMappings(),boundSql.getParameterObject());
            setParameters(countStmt, configuration, boundSql, boundSql.getParameterObject());
            ResultSet rs = countStmt.executeQuery();
            if (rs.next()) {
                total = rs.getInt(1);
            }
            rs.close();
            countStmt.close();
//			connection.close();
        }
        if (entity != null) {
            entity.setTotal(total);
        }

        Dialect.Type databaseType = null;
        try {
            databaseType = Dialect.Type.valueOf(configuration.getVariables()
                    .getProperty("dialect").toUpperCase());
        } catch (Exception e) {
            // ignore
        }
        if (databaseType == null) {
            databaseType = Dialect.Type.valueOf("oracle".toUpperCase());
            log.debug("没有配置的数据库类型，采用默认形式oracle");
        } else {
            log.debug("配置的数据库类型为 : " + databaseType);
        }
        Dialect dialect = null;
        String sql = "";
        switch (databaseType) {
            case ORACLE://oracle的处理方式
                dialect = new DBDialect();
                sql = dialect.getOracleString(originalSql, offset, limit);
                break;
            case MYSQL://mysql的处理方式
                dialect = new DBDialect();
                sql = dialect.getMysqlString(originalSql, offset, limit);
                break;
            default://默认的
                dialect = new DBDialect();
                sql = dialect.getOracleString(originalSql, offset, limit);
                break;
        }
        if (log.isDebugEnabled()) {
            log.debug("生成分页SQL : " + boundSql.getSql());
        }
        metaStatementHandler.setValue("delegate.boundSql.sql", sql);
        metaStatementHandler.setValue("delegate.rowBounds.offset",
                RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit",
                RowBounds.NO_ROW_LIMIT);

        return invocation.proceed();
    }

    /**
     * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
     *
     * @param ps
     * @param configuration
     * @param boundSql
     * @param parameterObject
     * @throws SQLException
     */
    private void setParameters(PreparedStatement ps, Configuration configuration, BoundSql boundSql, Object parameterObject) throws SQLException {
//        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
//            Configuration configuration = mappedStatement.getConfiguration();    
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    PropertyTokenizer prop = new PropertyTokenizer(propertyName);
                    if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) {
                        value = boundSql.getAdditionalParameter(prop.getName());
                        if (value != null) {
                            value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
                        }
                    } else {
                        value = metaObject == null ? null : metaObject.getValue(propertyName);
                    }
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    if (typeHandler == null) {
                        throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement ");
                    }
                    typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
                }
            }
        }
    }


    /*
     * (non-Javadoc)
     *
     * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
     */
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 判断是否分页
     *
     * @param parameter
     * @return
     */

    private boolean hasPaging(Object parameter) {
        if (parameter instanceof EntitySupport) {
            EntitySupport entity = (EntitySupport) parameter;

            if (entity.isNoPaging()) {
                return false;
            }

            int pageindex = entity.getPageIndex();
            int pagesize = entity.getPageSize();
            if (pageindex > -1 && pagesize > 0 && (pageindex + 1) * pagesize < RowBounds.NO_ROW_LIMIT) {
                return true;
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
     */
    public void setProperties(Properties arg0) {
    }

}